﻿//
// (C) Copyright 2009 Irantha Suwandarathna (iranthas@hotmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using EffiProz.Core.Errors;
using EffiProz.Core.Lib;

namespace EffiProz.Core.DataTypes
{
    public class BlobInputStream : Stream
    {
        BlobData blob;
        long availableLength;
        long bufferOffset;
        long currentPosition;
        byte[] buffer;
        bool isClosed;
        SessionInterface session;

        public BlobInputStream(SessionInterface session, BlobData blob,
                               long offset, long length)
        {

            this.session = session;
            if (!isInLimits(blob.length(session), offset, length))
            {
                throw new IndexOutOfRangeException();
            }

            this.blob = blob;
            this.availableLength = offset + length;
            this.currentPosition = offset;
        }

        public override int ReadByte()
        {

            if (currentPosition >= availableLength)
            {
                return -1;
            }

            if (buffer == null
                    || currentPosition >= bufferOffset + buffer.Length)
            {
                try
                {
                    checkClosed();
                    readIntoBuffer();
                }
                catch (CoreException e)
                {
                    throw new IOException(e.Message, e);
                }
            }

            int val = buffer[(int)(currentPosition - bufferOffset)] & 0xff;

            currentPosition++;

            return val;
        }

        public long skip(long n)
        {

            if (n <= 0)
            {
                return 0;
            }

            if (currentPosition + n > availableLength)
            {
                n = availableLength - currentPosition;
            }

            currentPosition += n;

            return n;
        }

        public int available()
        {
            return (int)(bufferOffset + buffer.Length - currentPosition);
        }

        public override void Close()
        {
            isClosed = true;
        }

        private void checkClosed()
        {

            if (isClosed || blob.isClosed())
            {
                throw Error.error(ErrorCode.X_0F503);
            }
        }

        private void readIntoBuffer()
        {

            long readLength = availableLength - currentPosition;

            if (readLength <= 0) { }

            if (readLength > session.getStreamBlockSize())
            {
                readLength = session.getStreamBlockSize();
            }

            buffer = blob.getBytes(session, currentPosition, (int)readLength);
            bufferOffset = currentPosition;
        }

        static bool isInLimits(long fullLength, long pos, long len)
        {
            return pos >= 0 && len >= 0 && pos + len <= fullLength;
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            long readLength = availableLength - currentPosition;

            if (readLength <= 0) { return 0; }

            if (readLength > session.getStreamBlockSize())
            {
                readLength = session.getStreamBlockSize();
            }
            if (readLength > count)
                readLength = count;

            byte[] tmp = blob.getBytes(session, currentPosition, (int)readLength);
            Array.Copy(tmp, 0, buffer, offset, tmp.Length);

            return tmp.Length;
           
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            if (origin == SeekOrigin.Begin)
            {
                if (offset >= 0)
                    currentPosition = offset;
                else               
                    currentPosition = availableLength - Math.Abs(offset);               

            }
            else if (origin == SeekOrigin.End)
            {
                if (offset >= 0)
                    currentPosition = availableLength - Math.Abs(offset);
                else               
                    currentPosition =  Math.Abs(offset);                

            }
            else if (origin == SeekOrigin.Current)
            {
                currentPosition = currentPosition + offset;               

            }

            currentPosition = offset;
            return currentPosition;
        }


        public override void Flush(){}
      

        public override long Position
        {
            get
            {
                return currentPosition;
            }

            set
            {
                currentPosition = value;
            }
        }

        public override long Length
        {
            get
            {
                return availableLength;
            }
        }

        public override bool CanRead
        {
            get
            {
                return true;
            }
        }

        public override bool CanSeek
        {
            get
            {
                return false;
            }
        }

        public override bool CanWrite
        {
            get
            {
                return true;
            }
        }
    
    }
}
